use std::{
    fs::File,
    io::{Read, Write},
};

use blake2::{
    digest::{InvalidOutputSize, Update, VariableOutput},
    VarBlake2b,
};
use chacha20::Key;
use getrandom::{getrandom, Error as GetRandomError};

pub trait NewKey: Sized {
    fn new() -> Result<Self, GetRandomError>;
    fn new_from_password(password: &str) -> Result<Self, InvalidOutputSize>;
    fn save_to_file(&self, path: &str) -> Result<(), Box<dyn std::error::Error>>;
    fn new_from_file(path: &str) -> Result<Self, Box<dyn std::error::Error>>;
}

pub type Key32 = Key;

impl NewKey for Key32 {
    fn new() -> Result<Self, GetRandomError> {
        let mut key = Self::default();
        getrandom(&mut key)?;
        Ok(key)
    }

    fn new_from_password(password: &str) -> Result<Self, InvalidOutputSize> {
        let mut key = Self::default();
        let mut hasher = VarBlake2b::new(32)?;
        hasher.update(password.as_bytes());
        hasher.finalize_variable(|out| {
            key.clone_from_slice(out);
        });
        Ok(key)
    }

    fn save_to_file(&self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
        let mut out = File::create(path)?;
        out.write_all(self)?;
        Ok(())
    }

    fn new_from_file(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
        let mut out = File::open(path)?;
        let mut key = Self::default();
        out.read_exact(&mut key)?;
        Ok(key)
    }
}
